{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 比例尺Scale\n",
    "\n",
    "从某种程度上讲，数据可视化就是将数据高效准确地映射为图形。D3提供多种称为`scale`的结构来支持从数据模型到可视化模型的映射。尺度到底是什么呢？简单来说，尺度可以视为数学函数。\n",
    "\n",
    ">如果f是一个从集合A到集合B的函数，我们说集合A是f的定义域，集合B是f的值域。如果f(a)=b，则我们称b为a的像，a是b的原像。函数f的值域或者像，是集合A中所有元素的像的集合。如果f是一个A到B的函数，那么我们称f为A到B的一个映射。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在数学中，x 的范围被称为定义域，y 的范围被称为值域。\n",
    "\n",
    "D3 中的比例尺，也有定义域和值域，分别被称为 domain 和 range。开发者需要指定 domain 和 range 的范围，如此即可得到一个计算关系。\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 连续比例尺\n",
    "\n",
    "最常用的尺度——连续尺度。连续尺度能够把一个连续的定义域映射到一个连续的值域，它包括线性尺度、幂级尺度、对数尺度和时间尺度。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "ename": "Error",
     "evalue": "Cannot find module 'jsdom'\nRequire stack:\n- /root/[eval]",
     "output_type": "error",
     "traceback": [
      "internal/modules/cjs/loader.js:955",
      "  throw err;",
      "  ^",
      "",
      "Error: Cannot find module 'jsdom'",
      "Require stack:",
      "- /root/[eval]",
      "    at Function.Module._resolveFilename (internal/modules/cjs/loader.js:952:15)",
      "    at Function.Module._load (internal/modules/cjs/loader.js:835:27)",
      "    at Module.require (internal/modules/cjs/loader.js:1012:19)",
      "    at require (internal/modules/cjs/helpers.js:72:18)",
      "    at evalmachine.<anonymous>:1:13",
      "    at Script.runInThisContext (vm.js:131:20)",
      "    at Object.runInThisContext (vm.js:297:38)",
      "    at run ([eval]:1054:15)",
      "    at onRunRequest ([eval]:888:18)",
      "    at onMessage ([eval]:848:13)"
     ]
    }
   ],
   "source": [
    "var jsdom = require('jsdom');\n",
    "const {\n",
    "    JSDOM\n",
    "} = jsdom;\n",
    "const {\n",
    "    document\n",
    "} = (new JSDOM('')).window;\n",
    "global.document = document;\n",
    "\n",
    "var d3 = require(\"d3\")\n",
    "d3.select(\"body\").append('div').attr(\"id\", \"linear\")\n",
    "d3.select(\"body\").append('div').attr(\"id\", \"linearCapped\")\n",
    "\n",
    "var max = 11,\n",
    "    data = [];\n",
    "for (var i = 1; i < max; ++i) data.push(i);\n",
    "\n",
    "var linear = d3.scaleLinear() // <-A\n",
    "    .domain([1, 10]) // <-B\n",
    "    .range([1, 10]); // <-C        \n",
    "var linearCapped = d3.scaleLinear()\n",
    "    .domain([1, 10])\n",
    "    .range([1, 24]); // <-D\n",
    "\n",
    "var pow = d3.scalePow().exponent(2); // <-E\n",
    "var powCapped = d3.scalePow() // <-F\n",
    "    .exponent(2)\n",
    "    .domain([1, 10])\n",
    "    .rangeRound([1, 10]); // <-G\n",
    "\n",
    "var log = d3.scaleLog(); // <-H\n",
    "var logCapped = d3.scaleLog() // <-I\n",
    "    .domain([1, 10])\n",
    "    .rangeRound([1, 10]);\n",
    "\n",
    "\n",
    "function render(data, scale, selector) {\n",
    "    d3.select(selector).selectAll(\"div.cell\")\n",
    "        .data(data)\n",
    "        .enter().append(\"div\").classed(\"cell\", true);\n",
    "\n",
    "    d3.select(selector).selectAll(\"div.cell\")\n",
    "        .data(data)\n",
    "        .exit().remove();\n",
    "\n",
    "    d3.select(selector).selectAll(\"div.cell\")\n",
    "        .data(data)\n",
    "        .style(\"display\", \"inline-block\")\n",
    "        .text(function(d) {\n",
    "            return scale(d).toFixed(1);\n",
    "        });\n",
    "}\n",
    "\n",
    "render(data, linear, \"#linear\");\n",
    "render(data, linearCapped, \"linearCapped\");\n",
    "\n",
    "$$.html(document.body.innerHTML);"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "d3.scaleLinear() 的返回值，是可以当做函数来使用的。参见：<https://github.com/d3/d3-scale/blob/v2.2.2/README.md#scaleLinear>，因此，才有这样的用法："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3.5555555555555554"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "linearCapped(2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 序数比例尺\n",
    "\n",
    "有时候，定义域和值域不一定是连续的。例如，姓名、地名等等，这些值都是离散的，线性比例尺不适合，需要用到序数比例尺。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "var index = [0, 1, 2, 3, 4];\n",
    "var color = [\"red\", \"blue\", \"green\", \"yellow\", \"black\"];\n",
    "\n",
    "var ordinal = d3.scaleOrdinal()\n",
    "        .domain(index)\n",
    "        .range(color);"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'yellow'"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "ordinal(3); "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Selection { _groups: [], _parents: [] }"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "var svg = d3.select(document.body).append('svg')\n",
    "    .attr('xmlns', 'http://www.w3.org/2000/svg')\n",
    "    .attr('width', 500)\n",
    "    .attr('height', 500);\n",
    "\n",
    "var marge = {\n",
    "top: 60,\n",
    "bottom: 60,\n",
    "left: 60,\n",
    "right: 60\n",
    "  \t}\n",
    "  \tvar dataset = [2.5, 2.1, 1.7, 1.3, 0.9];\n",
    "\n",
    "  \t//定义一个线性比例尺\n",
    "  \tvar scaleLinear = d3.scaleLinear()\n",
    ".domain([0, d3.max(dataset)])\n",
    ".range([0, 300]);\n",
    "\n",
    "  \tvar svg = d3.select(\"svg\");\n",
    "  \tvar g = svg.append(\"g\")\n",
    ".attr(\"transform\", \"translate(\" + marge.top + \",\" + marge.left + \")\");\n",
    "\n",
    "  \tvar rectHeight = 30;\n",
    "\n",
    "  \tg.selectAll(\"rect\")\n",
    ".data(dataset)\n",
    ".enter()\n",
    ".append(\"rect\")\n",
    ".attr(\"x\", 20)\n",
    ".attr(\"y\", function(d, i) {\n",
    "    return i * rectHeight;\n",
    "})\n",
    ".attr(\"width\", function(d) {\n",
    "    return scaleLinear(d); //设置宽,并在这里使用比例尺\n",
    "})\n",
    ".attr(\"height\", rectHeight - 5)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "ename": "ReferenceError",
     "evalue": "d3 is not defined",
     "output_type": "error",
     "traceback": [
      "evalmachine.<anonymous>:3",
      "　　var colorScale = d3.scaleLinear() // <-A",
      "                   ^",
      "",
      "ReferenceError: d3 is not defined",
      "    at evalmachine.<anonymous>:3:20",
      "    at Script.runInThisContext (vm.js:131:20)",
      "    at Object.runInThisContext (vm.js:297:38)",
      "    at run ([eval]:1054:15)",
      "    at onRunRequest ([eval]:888:18)",
      "    at onMessage ([eval]:848:13)",
      "    at process.emit (events.js:315:20)",
      "    at emit (internal/child_process.js:881:12)",
      "    at processTicksAndRejections (internal/process/task_queues.js:85:21)"
     ]
    }
   ],
   "source": [
    "var max = 21,\n",
    "    data = [];\n",
    "\n",
    "var colorScale = d3.scaleLinear() // <-A\n",
    "    .domain([0, max])\n",
    "    .range([\"white\", \"#4169e1\"]);\n",
    "\n",
    "var divergingScale = function(pivot) { // <-B\n",
    "    return d3.scaleLinear()\n",
    "        .domain([0, pivot, max]) // <-C\n",
    "        .range([\"white\", \"#4169e1\", \"white\"])\n",
    "};"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Javascript (Node.js)",
   "language": "javascript",
   "name": "javascript"
  },
  "language_info": {
   "file_extension": ".js",
   "mimetype": "application/javascript",
   "name": "javascript",
   "version": "13.13.0"
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": true,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": false,
   "toc_position": {},
   "toc_section_display": true,
   "toc_window_display": false
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
